#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "db.h"
#include "util.h"
#include "json.h"
#include "http.h"

static sqlite3 *sqlite_open(const char *dbfile) {
	sqlite3 *db = NULL;
	int retval = 0;

	retval = sqlite3_open(dbfile, &db);
	if (retval != SQLITE_OK) {
		printf("Can't open database: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		return NULL;
	}

	return db;
}

static int sqlite_begin_transaction(sqlite3 *db) {
	return sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
}

static int sqlite_commit_transaction(sqlite3 *db) {
	return sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
}

int sqlite_rollback_transaction(sqlite3 *db) {
	return sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
}

static int sqlite_bind(sqlite3_stmt *stmt, struct json_object *arr) {
	int retval = 0;
	int len = json_object_array_length(arr);
	int i = 0;
	for (i = 0; i < len; i++) {
		struct json_object *obj = json_object_array_get_idx(arr, i);
		switch (json_object_get_type(obj)) {
		case json_type_null: {
			sqlite3_bind_null(stmt, i + 1);
		}
			break;

		case json_type_string: {
			const char *str = json_object_get_string(obj);
			sqlite3_bind_text(stmt, i + 1, str, strlen(str), SQLITE_TRANSIENT);
		}
			break;

		case json_type_int: {
			int n = json_object_get_int(obj);
			sqlite3_bind_int(stmt, i + 1, n);
		}
			break;

		case json_type_double: {
			double n = json_object_get_double(obj);
			sqlite3_bind_double(stmt, i + 1, n);
		}
			break;

		case json_type_object: {
			const char *file = json_object_get_string(json_object_object_get(obj, "file"));
			void *blob = NULL;
			size_t size = 0;
			char *prefix = "http://";
			if (strncmp(file, prefix, strlen(prefix)) == 0) {
//				string_t str = http_download(file, "GET");
//				size = string_length(str);
//				blob = malloc(size + 1);
//				memcpy(blob, string_str(str), size);
//				string_free(str);
			} else {
//				FILE *fp = NULL;
//				if ((fp = fopen(file, "rb")) != NULL) {
//					size = get_file_size(file);
//					fseek(fp, 0, SEEK_SET);
//					blob = malloc(size + 1);
//					fread(blob, size, 1, fp);
//					fclose(fp);
//				}
			}
			if (blob) {
				sqlite3_bind_blob(stmt, i + 1, blob, size, SQLITE_TRANSIENT);
				free(blob);
			}
		}
			break;

		case json_type_array: {
			sqlite_bind(stmt, obj);
		}
			break;

		default:
			break;
		}
	}

	retval = sqlite3_step(stmt);
	if (retval != SQLITE_DONE) {
		return retval;
	}
	sqlite3_reset(stmt);

	return retval;
}

GString *sqlite_exec(const char *filename, const char *sql, ...) {
	sqlite3 *db = NULL;
	char *errmsg = NULL;
	int retval = 0;
	sqlite3_stmt *stmt = NULL;
	GString *result = NULL;
	struct json_object *arr = NULL;

	va_list argptr;
	struct json_object *params = NULL;

	db = sqlite_open(filename);
	if (!db) {
		return result;
	}

	retval = sqlite3_prepare(db, sql, -1, &stmt, (const char **) &errmsg);
	if (retval != SQLITE_OK) {
		printf("%s\n", errmsg);
		sqlite3_close(db);
		return result;
	}

	sqlite_begin_transaction(db);

	va_start(argptr, sql);
	params = va_arg(argptr, struct json_object *);
	if (params && json_object_is_type(params, json_type_array)) {
		retval = sqlite_bind(stmt, params);
	} else {
		retval = sqlite3_step(stmt);
	}
	va_end(argptr);

	sqlite_commit_transaction(db);

	arr = json_object_new_array();
	while (SQLITE_ROW == retval) {
		int colcount = sqlite3_column_count(stmt);
		int i = 0;
		struct json_object *obj = json_object_new_object();
		for (i = 0; i < colcount; i++) {
			int type = sqlite3_column_type(stmt, i);
			const char *column_name = sqlite3_column_name(stmt, i);
			switch (type) {
			case SQLITE_INTEGER:
				json_object_object_add(obj, column_name, json_object_new_int(sqlite3_column_int(stmt, i)));
				break;

			case SQLITE_FLOAT:
				json_object_object_add(obj, column_name, json_object_new_double(sqlite3_column_double(stmt, i)));
				break;

			case SQLITE_TEXT:
				json_object_object_add(obj, column_name,
						json_object_new_string((const char *) sqlite3_column_text(stmt, i)));
				break;

			case SQLITE_BLOB: {
//				const void *blob = sqlite3_column_blob(stmt, i);
//				int size = sqlite3_column_bytes(stmt, i);
//				unsigned char *str = base64_encode((unsigned char *) blob, size);
//				json_object_object_add(obj, column_name, json_object_new_string((const char *) str));
//				free(str);
			}
				break;

			case SQLITE_NULL:
				json_object_object_add(obj, column_name, NULL);
				break;

			default:
				json_object_object_add(obj, column_name,
						json_object_new_string((const char *) sqlite3_column_text(stmt, i)));
				break;
			}
		}
		json_object_array_add(arr, obj);
		retval = sqlite3_step(stmt);
	}

	result = g_string_new(json_object_to_json_string(arr));
	json_object_put(arr);

	sqlite3_finalize(stmt);
	sqlite3_close(db);

	return result;
}
